#pragma  once
#include<iostream>
#include<cstdlib>//exit
#include<sys/types.h>//ftok、shmat
#include<sys/shm.h>//ftok、shmget、shmctl、shmat
#include<sys/ipc.h>//shmget、shmctl
#include<unistd.h>//sleep
#define PATH "."
#define PROJID 0x66
#define SHM -1
#define USER "user"
#define ROOT "root"
#define ERR_EXIT(m)\
    do\
    {\
        perror(m);\
        exit(EXIT_FAILURE);\
    }while(0)

class shm
{
private:
    void Insert(int flag)//创建共享内存
    {
        _shm = shmget(_key,4096,flag);//失败返回-1
        if(_shm < 0)
        {
            //创建共享内存失败
            ERR_EXIT("shmget");
        }
        //创建共享内存成功
        printf("shm:%d\n",_shm);
    }
    void Del()//删除共享资源
    {
        int n = shmctl(_shm,IPC_RMID,nullptr);//失败返回-1
        if(n < 0)
        {
            ERR_EXIT("shmctl");
        }
        printf("删除资源成功\n");
    }
    void Relevance()//进程间关联
    {
        _start_mem = shmat(_shm,nullptr,0);//失败返回-1
        if((long long)_start_mem < 0)
        {
            ERR_EXIT("shmat");
        }
        printf("进程关联成功\n");
    }
    void DisRelevance()//取消进程之间的关联
    {
        int n = shmdt(_start_mem);
        if(n < 0)
        {
            ERR_EXIT("shmdt");
        }
        printf("进程断联成功\n");
    }

public:
    shm(const std::string& name)
    :_shm(SHM)
    ,_start_mem(nullptr)
    ,_name(name)
    {
         _key = ftok(PATH,PROJID);//失败返回－1
        if(_key < 0)
        {
            //key值申请失败
            ERR_EXIT("ftok");
        }
        //key值申请成功
        printf("key:0x%x\n",_key);
        if(_name == ROOT)
        {
            Insert(IPC_CREAT | IPC_EXCL | 0666);
        }
        else
        {
            Insert(IPC_CREAT);
        }
        Relevance();
    }
    void* add()//返回虚拟内存首地址
    {
        return _start_mem;
    }

    ~shm()
    {
        std::cout << "11111111" << std::endl;
        DisRelevance();
        if(_name == ROOT)
        {
            Del();
        }
    }
private:
    key_t _key;//随机的key值
    int _shm;//用户用来管理进程的shmid
    void* _start_mem;//申请的虚拟地址的起始地址
    std::string _name;//用户身份描述符
};